Computer system testing generally seeks to uncover design and implementation errors by creating processing conditions that attempt to replicate real-world conditions. Oftentimes the time and resources required to adequately test a system are strained because of product delivery commitments. Thus, developing automated test suites with thorough coverage of system functionality is very important.
One system-level function that is usually the object of a system test is the functionality that manages the memory or “main storage” of a data processing system. Testing the main storage management functionality of a data processing system is extremely complex. The main storage management implemented in the 2200 Series data processing systems from Unisys is used as an example to illustrate the complexity.
The 2200 operating system contains a complex memory management system that maintains its system memory on behalf of itself and all its users. In order for a program to access physical memory, two conditions must be met. First, the addresses within the program must be translated and adjusted to meet the architectural and physical requirements of the system memory. Second, the requirements for accessibility must be met.
When a program is written it is normally written as a collection of individual instruction groups called subroutines. Addresses within these subroutines are either subroutine relative or symbolic if they reference another subroutine. When a compiler, or assembler, compiles or assembles these subroutines into a machine language object module, the object module is divided into logical groups called banks. Each bank contains a number of locations with each location being identifiable by its address. The compiler or assembler collects a number of subroutines in each bank and assigns each bank a range of addresses consisting of a logical bank number (LBN) and an offset relative to the start address of that particular bank.
A compiled program resides in a program file on system mass storage in the form of an object module. It consists of a number of virtual (logical) banks created by a compiler or assembler. A typical object module includes a code bank, a data bank, a link vector bank and a symbolic debugging dictionary bank.
In order for a program to be executed, each of the program's banks must be loaded into the system's physical memory. To do so, each program bank must have all symbolic references resolved. Any address relocation needed due to a subroutine's position within a bank or duplicate addresses within multiple banks must be completed. The process of resolving address references is called linking. The bank's addresses must then be translated into a form that coincides with the system architecture (L, BDI, Offset). These addresses are called virtual addresses because they do not relate to a specific set of physical storage locations. Linking may be performed prior to a program's execution (“static linking”). This removes the overhead inherent in assigning and translating addresses each time the program is executed. However, under certain conditions, it may be desirable to perform this function at execution time (“dynamic linking”).
When a program is executed its virtual banks must be loaded into physical memory. To do so a physical bank is built from each virtual bank and then loaded into memory. When building each physical bank, its virtual addresses must be translated to the physical locations in system memory to be occupied. Each physical bank has associated with it a number of properties including type, size, limits, protection level, sharing level, storage type, and access level. These properties are stored within a data structure called a “bank descriptor,” which is located within another data structure called a “bank descriptor table.” Each bank descriptor in a bank descriptor table (BDT) is accessed via an index called the bank descriptor index (BDI).
Within the 2200 system architecture the addressing environment is managed within 8 levels (L0–L7) with each level relating to the degree of access locality. A hardware structure exists for the pointer to the bank descriptor table for each access level. From the user's perspective, the locality levels are activity, program, application and system. This means that certain physical banks can be accessed only by a given activity within a multi-activity program, that certain banks can be accessed by all activities within a program, and that certain banks can be accessed by all activities within multiple programs. The system level banks are not directly accessible by a program. Banks built at level 7 are the most local.
It is often desirable to create a program to perform a function that can be accessed by multiple users with the appropriate security and access privileges but is partitioned from the rest of the system. Within the 2200 operating system it is possible to create such a structure. The structure includes a group of shared object modules and a definition element and is called a fixed gate subsystem. Access to a fixed gate subsystem is controlled by a combination of hardware and software mechanisms. The entry points to such a structure are fixed hence the term ‘fixed gate’ subsystem. The owner of the subsystem controls how it is accessed and how it is protected from other users and subsystems. Banks within a fixed gate subsystem are built at application level. Ordinary (non-shared) object modules are executed within a special default subsystem (not a fixed gate subsystem) called the ‘home subsystem’.
As the 2200 operating system supports virtual memory addresses larger than the available physical memory, programs can be constructed that exceed the size of the system's physical memory. This means that not all banks of a given program can be loaded in memory simultaneously and remain for the duration of a program. When a program is first executed, its initial bank group is loaded. The timing of how and when additional banks are loaded into the system memory is determined by how the banks are linked and referenced.
In addition to the banks that exist within a program at execution time, new banks may be dynamically created, deleted and have their properties modified from within the program. Banks that exist at program execution can only be modified.
Testing the memory and related aspects of large multi-processor systems, such as the 2200 series, with their vast number of supporting structures and mechanisms presents a major problem. The capability to execute many programs simultaneously, each having the ability to access memory and alter many of the supporting structures creates an almost infinite number of inter-related operational sequences. It is impossible to predict the timing of such sequences let alone creating a sufficient number of these sequences to provide a high level of confidence in the system's integrity.
Traditionally, system memory management has been tested using a number of individual programs, each performing one or more specific functions. Given the complexity of the memory management system, whether a given combination of programs would create a desired test condition has largely been a matter of chance.
A method and apparatus that addresses the aforementioned problems, as well as other related problems, are therefore desirable.